package com.enerbos.dsl.service.impl;

import com.enerbos.authorise.rbac.model.AuthoriseOrganization;
import com.enerbos.authorise.rbac.model.AuthoriseSite;
import com.enerbos.authorise.rbac.service.AuthoriseSiteService;
import com.enerbos.driver.kafka.producer.EnerbosProducer;
import com.enerbos.dsl.model.CalculationConfig;
import com.enerbos.dsl.model.CalculationTemplate;
import com.enerbos.dsl.repository.CalculationConfigRepository;
import com.enerbos.dsl.repository.CalculationTemplateRepository;
import com.enerbos.dsl.service.BaseService;
import com.enerbos.persistence.FilterDescriptor;
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.Predicate;
import java.text.ParseException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * All rights Reserved, Designed By 翼虎能源
 * Copyright:    Copyright(C) 2015-2017
 * Company   北京翼虎能源科技有限公司
 *
 * @author hk
 * @version 1.0
 * @date 2017-02-23 10:31
 * @Description
 */
@Service
public class BaseServiceImpl implements BaseService {

    @Autowired
    private CalculationConfigRepository calculationConfigRepository;
    @Autowired
    private CalculationTemplateRepository calculationTemplateRepository;

    @Autowired
    private EnerbosProducer enerbosProducer;

    @Autowired
    private AuthoriseSiteService authoriseSiteService;

    @Override
    public List<CalculationTemplate> getCalculationTemplateList() {
        return calculationTemplateRepository.findAll();
    }

    @Override
    public List<CalculationConfig> getCalculationConfigList(String name) {
        Specification<CalculationConfig> spec = (root, criteriaQuery, cb) -> {
            Predicate p1 = StringUtils.isEmpty(name) ? cb.isNotNull(root.get("name")) : cb.like(root.get("name"), String.format("%%%1$s%%", name));
            criteriaQuery.where(p1);
            //criteriaQuery.orderBy(cb.desc(root.get("score")));
            return criteriaQuery.getRestriction();
        };

        return calculationConfigRepository.findAll(spec);
    }

    @Override
    public String buildConfig(String templateId, String name, String expression, String expressionTemplate, String orgId, String siteId, List<String> params) {
        CalculationTemplate template = calculationTemplateRepository.findOne(templateId);

        if (template == null) {
            throw new RuntimeException("请选择规则模板！");
        }

        //处理表达式
        String process_expression = buildExpression(expression, expressionTemplate);

        //处理DSL脚本
        String template_body = buildTemplate(template.getTemplate(), process_expression, name, orgId, siteId, params);

        return template_body;
    }

    @Override
    public String sendConfig(String templateId, String name, String expression, String expressionTemplate, String orgId, String siteId, List<String> params) {
        String DSL_Body = buildConfig(templateId, name, expression, expressionTemplate, orgId, siteId, params);
        String DSL_Template = "{" +
                "    \"msgid\": \"msg_rule_run\"," +
                "    \"msgbody\": {" +
                "        \"rule\": \"%1$s\",         " +   //rule name
                "        \"dsl\": \"%2$s\"," +
                "        \"orgid\": \"%3$s\"," +
                "        \"siteid\": \"%4$s\"," +
                "        \"privilege\": 0," +
                "        \"toprivilege\": 0" +
                "    }," +
                "    \"occurDate\": \"%5$s\",  " +     //当前时间(下发时间)
                "    \"from\": \"mod_rule_engine_push\"," +
                "    \"to_id\": \"00000000-0000-0000-0000-000000000000\"," +
                "    \"to\": \"mod_rule_engine_dispatch\"" +
                "}";
        String msg = String.format(DSL_Template, name, DSL_Body, orgId, siteId, DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));

        enerbosProducer.sendStringMsg(msg);
        return msg;
    }

    @Override
    public List<Map<String, Object>> getCompanyInfoList(String filter, Integer pageNum, Integer pageSize) {
        Pageable pageable = new PageRequest(pageNum, pageSize);
        List<FilterDescriptor> filterList = new ArrayList<>();
        if (!StringUtils.isEmpty(filter)) {
            filterList.add(new FilterDescriptor("siteName", filter, FilterDescriptor.FilterOperator_CONTAINS));
        }

        Page<AuthoriseSite> sitePage = authoriseSiteService.findSiteList(filterList, pageable);
        List<Map<String, Object>> result = new ArrayList<>(sitePage.getNumberOfElements());

        sitePage.getContent().forEach(_item -> {
            Map<String, Object> _itemMap = new HashMap<>();
            _itemMap.put("siteId", _item.getId());
            _itemMap.put("siteName", _item.getSiteName());

            AuthoriseOrganization organization = _item.getAuthoriseOrganization();
            _itemMap.put("orgId", organization.getId());
            _itemMap.put("orgName", organization.getOrgName());

            result.add(_itemMap);
        });

        return result;
    }

    private String buildExpression(String expression, String expressionTemplate) {
        expressionTemplate = StringUtils.isEmpty(expressionTemplate) ? "get_history_data_by_tag(\\\\\"%1$s\\\\\",lasttime, lasttime)" : expressionTemplate;

        String _expression = expression.replaceAll("(\\[@[a-zA-Z\\d]+\\]$)", "");
        Pattern p = Pattern.compile("([a-zA-Z0-9_#]+)");
        Matcher m = p.matcher(_expression);

        Set<String> tagNames = new HashSet<>();
        while (m.find()) {
            for (int i = 0, len = m.groupCount(); i < len; i++) {
                tagNames.add(m.group(i));
            }
        }

        for (String _tagName : tagNames) {
            _expression = _expression.replaceAll(_tagName, String.format(expressionTemplate, _tagName));
        }

        return _expression;
    }

    private String buildTemplate(String template, String expression, String name, String orgId, String siteId, List<String> params) {
        params = params == null ? new ArrayList<>() : params;

        String val;
        for (int i = 0, len = params.size(); i < len; i++) {
            val = params.get(i);

            if (val == null) {
                params.set(i, "");
            } else {
                if (val.startsWith("_time:")) {
                    String _timeStr = val.substring(6);

                    if (_timeStr.contains("#")) {
                        String[] valueArray = _timeStr.split("#");

                        if (valueArray.length != 2) {
                            throw new RuntimeException("日期参数填写错误，请检查！ 模板：_time:-1#d");
                        }

                        Date baseDate = new Date();
                        switch (valueArray[1]) {
                            case "d" : {
                                params.set(i, DateFormatUtils.format(DateUtils.addDays(baseDate, Integer.valueOf(valueArray[0])), "yyyy-MM-dd 00:00:00"));
                            } break;
                            case "m" : {
                                params.set(i, DateFormatUtils.format(DateUtils.addMonths(baseDate, Integer.valueOf(valueArray[0])), "yyyy-MM-dd 00:00:00"));
                            } break;
                            default: {
                                params.set(i, DateFormatUtils.format(DateUtils.addDays(baseDate, -1), "yyyy-MM-dd 00:00:00"));
                            } break;
                        }
                    } else {
                        Date baseDate;
                        try {
                            baseDate = DateUtils.parseDate(_timeStr, new String[]{"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss"});
                        } catch (ParseException e) {
                            baseDate = DateUtils.addDays(new Date(), -1);
                        }

                        params.set(i, DateFormatUtils.format(baseDate, "yyyy-MM-dd HH:mm:ss"));
                    }
                } else if ("_expression".equalsIgnoreCase(val)) {
                    params.set(i, expression);
                } else if ("_rule".equalsIgnoreCase(val)) {
                    params.set(i, name);
                } else if ("_orgId".equalsIgnoreCase(val)) {
                    params.set(i, orgId);
                }  else if ("_siteId".equalsIgnoreCase(val)) {
                    params.set(i, siteId);
                }
            }
        }

        return String.format(template, params.toArray());
    }

}
